Passed
Pull Request — master (#101)
by Mark
01:52
created

Model.select   A

Complexity

Conditions 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
dl 0
loc 9
rs 10
c 0
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
A Model.registerIndex 0 3 1
1
import HasManyThrough from './Model/Relation/HasManyThrough';
2
import HasMany from "./Model/Relation/HasMany";
3
import BelongsTo from "./Model/Relation/BelongTo";
4
import MorphOne from "./Model/Relation/MorphOne";
5
import HasOne from "./Model/Relation/HasOne";
6
import HasOneThrough from "./Model/Relation/HasOneThrough";
7
import MorphTo from "./Model/Field/MorphTo";
8
import Field from "./Model/Field";
9
import Relation from "./Model/Relation";
10
import ForeignKey from "./Model/Field/ForeignKey";
11
import Index from "./Table/Index";
12
import {ModelInterface, ModelStaticInterface} from "../JeloquentInterfaces";
13
import Collection from "./Collection";
14
import * as Str from "../Util/Str";
15
import * as Obj from "../Util/Obj";
16
import * as ModelSetup from "./Model/Util/Setup";
17
import Table from "./Table";
18
19
class Model implements ModelInterface {
20
21
    private static kebabCaseName: string;
22
23
    private static snakeCaseName: string;
24
25
    _tmpId: string;
26
27
    ['constructor']: ModelStaticInterface;
28
29
    private _originalFields: Field[];
30
31
    private _primaryFields: Field[];
32
33
    private numberOfFields: number;
34
35
    constructor(fields: Field[] = []) {
36
        this.setFields(ModelSetup.addRelationFieldsToList(fields));
37
        this._tmpId = `_${++globalThis.Store.numberOfModelCreated}`;
38
    }
39
40
    static get className(): string {
41
        return this.name;
42
    }
43
44
    static get kebabCaseClassName(): string {
45
        return this.kebabCaseName ??= Str.KebabCase(this.className);
46
    }
47
48
    static get snakeCaseClassName(): string {
49
        return this.snakeCaseName ??= Str.SnakeCase(this.className);
50
    }
51
52
    get className(): string {
53
        return this.constructor.className;
54
    }
55
56
    get dirtyFieldNames(): string[] {
57
        return this.dirtyFields.map(field => field.name);
58
    }
59
60
    get dirtyFields(): Field[] {
61
        return this.originalFields.filter(field => field.isDirty);
62
    }
63
64
    get kebabCaseClassName(): string {
65
        return this.constructor.kebabCaseClassName;
66
    }
67
68
    get originalFields(): Field[] {
69
        return this._originalFields;
70
    }
71
72
    get originalPrimaryKey(): unknown {
73
        return this.primaryFields.reduce((toValue, field, i) => {
74
            if (i > 0) {
75
                return `${toValue}-${field.originalValue}`;
76
            }
77
            return field.originalValue;
78
        }, '') ?? this._tmpId ?? null;
79
    }
80
81
    get originalValues(): object {
82
        return this.originalFields.reduce((originalValues, field) => {
83
            if (field.originalValue !== undefined) {
84
                originalValues[field.name] = field.originalValue;
85
            }
86
            return originalValues;
87
        }, {});
88
    }
89
90
    get primaryFields(): Field[] {
91
        return this._primaryFields ??= this.originalFields.filter(field => field.isPrimary);
92
    }
93
94
    get primaryKey(): string {
95
        return this.primaryFields.reduce((toValue:string, field:Field, i:number): string => {
96
            if (i > 0) {
97
                return `${toValue}-${field.value}`;
98
            }
99
100
            if (field.value === null) {
101
                return field.value;
102
            }
103
104
            return `${field.value}`;
105
        }, '') ?? this._tmpId ?? null;
106
    }
107
108
    get primaryKeyName(): string[] {
109
        return this.originalFields.filter(field => field.isPrimary).map(field => field.name);
110
    }
111
112
    get snakeCaseClassName(): string {
113
        return this.constructor.snakeCaseClassName;
114
    }
115
116
    static aSyncInsert(data: object): Promise<Collection> {
117
        return new Promise((resolve) => {
118
            queueMicrotask(() => {
119
                resolve(this.insert(data));
120
            });
121
        });
122
    }
123
124
    static aSyncUpdate(data: object): Promise<Collection> {
125
        return new Promise((resolve) => {
126
            queueMicrotask(() => {
127
                resolve(this.update(data));
128
            });
129
        });
130
    }
131
132
    static all(): Collection {
133
        return globalThis.Store.database().all(this.className);
134
    }
135
136
    static delete(id): void {
137
        globalThis.Store.database().delete(this.className, id);
138
    }
139
140
    static find(id): unknown {
141
        return globalThis.Store.database().find(this.className, id);
142
    }
143
144
    static getIndexByKey(indexName) {
145
        return globalThis
146
            .Store
147
            .database()
148
            .getIndexByKey(this.className, indexName);
149
    }
150
151
    static getInstance(): ModelInterface {
152
        const original = globalThis.Store.classInstances[this.className] ?? (globalThis.Store.classInstances[this.className] = new this())
153
        const fieldsClone = original.originalFields.reduce((obj, field) => {
154
            obj.push(Object.assign(Object.create(Object.getPrototypeOf(field)), field));
155
            return obj;
156
        }, [])
157
158
        return Object.create(Object.getPrototypeOf(original)).setFields(fieldsClone);
159
    }
160
161
    static ids() {
162
        return globalThis
163
            .Store
164
            .database()
165
            .ids(this.className);
166
    }
167
168
    static insert(data: object|object[]): Collection {
169
        const modelsData = Array.isArray(data) ? data : [data];
170
        const length = modelsData.length;
171
        const models = new Collection();
172
        for (let i = 0; i < length; i++) {
173
            const modelData = modelsData[i];
174
            const model = this.getInstance();
175
            model.fill(modelData);
176
            globalThis.Store.database().insert(this.className, model);
177
            model.fillRelations(modelData);
178
            models.push(model);
179
        }
180
        return models;
181
    }
182
183
    static registerIndex(name: string): void {
184
        Index.register(this.getInstance(), name);
185
    }
186
187
    static update(data: object|object[]): Collection {
188
        const modelsData = Array.isArray(data) ? data : [data];
189
        const length = modelsData.length;
190
        const models = new Collection();
191
        for (let i = 0; i < length; i++) {
192
            const model = this.getInstance();
193
            model.fill(data);
194
            globalThis.Store.database().update(this.className, model);
195
            model.fillRelations(data);
196
            models.push(model);
197
        }
198
        return models;
199
    }
200
201
    delete() {
202
        this.constructor.delete(this.primaryKey);
203
    }
204
205
    fill(data) {
206
        for (let i = 0; i < this.numberOfFields; i++) {
207
            if (!(this.originalFields[i] instanceof Relation)) {
208
                const fieldName = this.originalFields[i].name;
209
                if (data[fieldName] !== undefined) {
210
                    this[`_${fieldName}`] = data[fieldName];
211
                }
212
            }
213
        }
214
    }
215
216
    fillRelations(data: object): void {
217
        // insert through relations after model insert;
218
        for (let i = 0; i < this.numberOfFields; i++) {
219
            if ((this.originalFields[i] instanceof Relation)) {
220
                const fieldName = this.originalFields[i].name;
221
                if (data[fieldName] !== undefined) {
222
                    this[`_${fieldName}`] = data[fieldName];
223
                }
224
            }
225
        }
226
    }
227
228
    isDirty(fieldName:string = null) {
229
        if (fieldName) {
230
            return this.dirtyFieldNames.includes(fieldName);
231
        }
232
        return this.dirtyFields.length > 0;
233
    }
234
235
    jsonStringify(): string {
236
        return JSON.stringify(this.toObject());
237
    }
238
239
    registerIndex(name) {
240
        Index.register(this, name);
241
    }
242
243
    resetDirty() {
244
        this.originalFields.filter((field) => !(field instanceof Relation)).forEach(field => {
245
            field.resetDirty();
246
        })
247
    }
248
249
    save() {
250
        globalThis.Store.database().save(this.className, this);
251
    }
252
253
    setFields(fields: Field[]) {
254
        ModelSetup.setFields(this, fields);
255
        return this;
256
    }
257
258
    tableSetup(table: Table) {
259
        ModelSetup.setupTable(this, table);
260
    }
261
262
    toJSON(): object {
263
        return this.toObject();
264
    }
265
266
    toObject(fromRelation = false): object {
267
        return Obj.fromModel(this, fromRelation);
268
    }
269
}
270
271
export {
272
    Model,
273
    Field,
274
    Relation,
275
    BelongsTo,
276
    HasOne,
277
    HasOneThrough,
278
    HasMany,
279
    HasManyThrough,
280
    MorphOne,
281
    MorphTo,
282
    ForeignKey,
283
};